package org.zhaya.sdk.processors;

import io.logz.sawmill.Doc;
import io.logz.sawmill.ProcessResult;
import io.logz.sawmill.Processor;
import io.logz.sawmill.annotations.ProcessorProvider;
import io.logz.sawmill.exceptions.ProcessorConfigurationException;
import io.logz.sawmill.utilities.JsonUtils;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.zhaya.sdk.processors.custom.SourceCodeTypeEnum;
import org.zhaya.sdk.util.GroovyFactory;
import org.zhaya.sdk.util.JavaScriptFactory;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.Objects;

import static java.util.Objects.requireNonNull;

/**
 * @author jackl
 */
@ProcessorProvider(type = "script", factory = ScriptProcessor.Factory.class)
public class ScriptProcessor implements Processor {
    private final Object pluginProcess;
    private final Method processMethod;

    public ScriptProcessor(Object pluginProcess, Method processMethod) {
        this.pluginProcess = requireNonNull(pluginProcess, "pluginProcess cannot be null");
        this.processMethod = requireNonNull(processMethod, "processMethod cannot be null");
    }

    @Override
    public ProcessResult process(Doc doc) throws InterruptedException {
        if (ObjectUtils.anyNull(this.pluginProcess, this.processMethod)) {
            throw new ProcessorConfigurationException("scriptProcessor config error, pluginProcess/processMethod cannot be null");
        }
        try {
            processMethod.invoke(pluginProcess, doc);
        } catch (Exception e) {
            return ProcessResult
                    .failure(String.format("failed to invoke script method, error info: [%s]", e.getCause().getMessage()));
        }
        return ProcessResult.success();
    }

    public static class Factory implements Processor.Factory {

        @Override
        public Processor create(Map<String, Object> config) {
            Configuration conf = JsonUtils.fromJsonMap(Configuration.class, config);
            if (StringUtils.isEmpty(conf.sourceCode)) {
                throw new ProcessorConfigurationException("scriptProcessor config error, sourceCode cannot be null");
            }
            if (Objects.isNull(conf.sourceCodeType)) {
                throw new ProcessorConfigurationException("scriptProcessor config error, sourceCodeType cannot be null");
            }
            Object pluginProcess = null;
            Method processMethod = null;
            try {
                switch (conf.sourceCodeType) {
                    case Java:
                        pluginProcess = GroovyFactory.getObject(conf.sourceCode);
                        break;
                    case python:
                    case python3:
                        break;
                    case javaScript:
                        pluginProcess = JavaScriptFactory.getPluginProcessor(conf.sourceCode);
                        break;
                    default:
                }
                if (Objects.nonNull(pluginProcess)) {
                    Class<?> processClass = pluginProcess.getClass();
                    processMethod = processClass.getDeclaredMethod("process", Doc.class);
                }
            } catch (Exception e) {
                throw new ProcessorConfigurationException("scriptProcessor config error", e);
            }
            return new ScriptProcessor(pluginProcess, processMethod);
        }
    }

    @Data
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Configuration implements Processor.Configuration {
        private String sourceCode;
        private SourceCodeTypeEnum sourceCodeType;

    }

}